<template>
  <div class="d-flex-col">
    <div class="header m-b-10">
      <el-button @click="chronology.undo()" :disabled="!canUndo"
        >撤销</el-button
      >
      <el-button @click="chronology.redo()" :disabled="!canRedo"
        >重做</el-button
      >
      <el-button @click="chronology.revert()" :disabled="!canRevert"
        >清空步骤并回到初始</el-button
      >
      <el-button @click="chronology.clear()" :disabled="!canClear"
        >清空步骤</el-button
      >
    </div>
    <div class="flex-grow d-flex-col">
      <div class="flex-grow panel m-b-10 d-flex-col">
        <Table1
          ref="table1"
          :sync-datas="datas => handleDatasReady(datas, 'table1')"
          :add-doc="data => handleAddDoc(data, 'table1')"
          :update-doc="data => handleUpdateDoc(data, 'table1')"
          :delete-doc="data => handleDeleteDoc(data, 'table1')"
        ></Table1>
      </div>
      <div class="panel d-flex-col" style="height:400px">
        <!-- <Table2
          ref="table2"
          :sync-datas="datas => handleDatasReady(datas, 'table2')"
          :add-doc="data => handleAddDoc(data, 'table2')"
          :update-doc="data => handleUpdateDoc(data, 'table2')"
          :delete-doc="data => handleDeleteDoc(data, 'table2')"
        ></Table2> -->
      </div>
    </div>
  </div>
</template>

<script>
import pouchdb from "@/utils/pouchdb";
import Chronology from "@/utils/chronology";
import Table1 from "./components/Table1.vue";
// import Table2 from "./components/Table2.vue";
import ioClient from "socket.io-client";
export default {
  components: {
    Table1
    // Table2
  },
  data() {
    return {
      // pouchdb
      myDB: new Map(),
      // chronology
      chronology: null,
      canUndo: false,
      canRedo: false,
      canClear: false,
      canRevert: false,
      isDBAddFirst: false,
      isFirstUpdate: false,
      // websocket
      io: null
    };
  },
  methods: {
    async syncDBDocs(name, datas) {
      const curDB = this.myDB.get(name);
      let res = await curDB.getBulkDocs();
      if (!res.total_rows) {
        await curDB.addBulkDocs(datas);
        res = await curDB.getBulkDocs();
      }
      return res.rows.map(item => item.doc);
    },
    addDBDoc(data, name) {
      const curDB = this.myDB.get(name);
      return new Promise((resolve, reject) => {
        curDB.addDoc(data).then(res => {
          const newRow = Object.assign({}, data, {
            _rev: res.rev
          });
          resolve(newRow);
        });
      });
    },
    updateDBDoc(data, name) {
      const curDB = this.myDB.get(name);
      return new Promise((resolve, reject) => {
        curDB.getDoc(data._id, { include_docs: true }).then(res => {
          const newRow = Object.assign({}, data, {
            _rev: res._rev
          });
          curDB.updateDoc(newRow).then(updateRes => {
            const updateRow = Object.assign({}, newRow, {
              _rev: updateRes._rev
            });
            resolve([res, updateRow]);
          });
        });
      });
    },
    deleteDBDoc(data, name) {
      const curDB = this.myDB.get(name);
      return new Promise((resolve, reject) => {
        curDB.getDoc(data._id).then(res => {
          curDB.deleteDoc(res._id, res._rev).then(() => {
            resolve(res._id);
          });
        });
      });
    },
    createChronology() {
      this.chronology = new Chronology({
        onEnd: () => {
          this.canRedo = false;
        },
        onRedo: () => {
          this.canUndo = true;
        },
        onBegin: () => {
          this.canUndo = false;
        },
        onUndo: () => {
          this.canRedo = true;
        },
        onAdd: () => {
          this.canUndo = true;
          this.canRevert = true;
          this.canClear = true;
        },
        onClear: () => {
          this.canUndo = false;
          this.canRedo = false;
          this.canRevert = false;
          this.canClear = false;
        }
      });
    },
    async handleDatasReady(datas, name) {
      this.myDB.set(name, new pouchdb(name));
      const transDatas = await this.syncDBDocs(name, datas);
      return transDatas;
    },
    handleAddDoc(data, name) {
      const { addDBDoc, deleteDBDoc, chronology } = this;
      const childTable = this.$refs[name];
      this.isDBAddFirst = true;
      chronology.add({
        up: () => {
          addDBDoc(data, name).then(newRow => {
            childTable.addData.call(childTable, newRow, this.isDBAddFirst);
          });
        },
        down: () => {
          this.isDBAddFirst = false;
          deleteDBDoc(data, name).then(_id => {
            childTable.deleteData.call(childTable, _id);
          });
        }
      });
    },
    handleUpdateDoc(data, name) {
      const { updateDBDoc, chronology } = this;
      const childTable = this.$refs[name];
      this.isFirstUpdate = true;
      updateDBDoc(data, name).then(([oldData, newData]) => {
        chronology.add({
          up: () => {
            if (this.isFirstUpdate) {
              this.isFirstUpdate = false;
              childTable.updateData.call(childTable, oldData, newData);
            } else {
              updateDBDoc(newData, name).then(([oldData, newData]) => {
                childTable.updateData.call(childTable, oldData, newData);
              });
            }
          },
          down: () => {
            updateDBDoc(oldData, name).then(([oldData, newData]) => {
              childTable.updateData.call(childTable, oldData, newData);
            });
          }
        });
      });
    },
    handleDeleteDoc(data, name) {
      const { addDBDoc, deleteDBDoc, chronology } = this;
      const childTable = this.$refs[name];
      chronology.add({
        up: () => {
          deleteDBDoc(data, name).then(_id => {
            childTable.deleteData.call(childTable, _id);
          });
        },
        down: () => {
          delete data._rev;
          addDBDoc(data, name).then(newRow => {
            childTable.addData.call(childTable, newRow);
          });
        }
      });
    },
    connect() {
      this.io = ioClient(this.$baseUrl);
      this.io.on("connect", () => {
        console.log("connect!");
        this.io.emit("aaa", "aaa", "bbb");
      });
      this.io.on("bbb", data => console.log(data));
    }
  },
  created() {
    this.createChronology();
    // this.connect();
  },
  beforeDestroy() {
    // 页面销毁前清空缓存数据
    if (this.myDB?.size) {
      [...this.myDB.values()].map(db => {
        db.destroy();
      });
    }
    // 页面销毁前清空缓存步骤
    this.chronology && this.chronology.clear();
  }
};
</script>

<style></style>
